﻿using System;
using System.Collections.Generic;
using Castle.Core.Logging;
using LIMS.DataAccess;
using LIMS.Entities;
using NHibernate.Criterion;

namespace LIMS.Service
{
    public class UserManagementService :IUserManagementService
    {
        //Private DBA interfaces
        private IDaoFactory oDBFactory;
        private IUserDao oUserDBA;
        private IGroupDAO oGroupABA;
        private IUserGroupLinkDAO oUserGrpDBA;
        private IGroupPermissionLinkDao oGroupPermissionDBA;
        private IMailService mailService;
        private ILogger logger;
        /// <summary>
        /// Constructor of UserManagementService
        /// </summary>
        /// <param name="oDBOp"></param>
        /// <param name="oMailService"></param>
        /// <param name="oLogger"></param>
        public UserManagementService(IDaoFactory oDBOp, IMailService oMailService, ILogger oLogger)
        {
            this.oDBFactory = oDBOp;
            this.mailService = oMailService;
            oUserDBA = oDBFactory.GetUserDao();
            oGroupABA = oDBFactory.GetGroupDao();
            oUserGrpDBA = oDBFactory.GetUserGroupLinkDAO();
            oGroupPermissionDBA = oDBFactory.GetGroupPermissionLinkDao();
            logger = oLogger;
        }

        /// <summary>
        /// Gets list of all users
        /// </summary>
        /// <returns></returns>
        public List<User> GetAllUsers()
        {
            List<User> myusers = oUserDBA.GetAll();
            return myusers;
        }

        /// <summary>
        /// Gets a user by Id
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public User GetUser(int Id)
        {
            return oUserDBA.GetById(Id);
        }
        /// <summary>
        /// Get all user groups
        /// </summary>
        /// <returns></returns>
        public List<Group> GetAllUserGroups()
        {
            return oGroupABA.GetAll();
        }
        /// <summary>
        /// Get user group by Id
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public Group GetUserGroupById(int Id)
        {
            return oGroupABA.GetById(Id);
        }

        /// <summary>
        /// Get user password by Id
        /// </summary>
        /// <param name="Id"></param>
        /// <returns></returns>
        public string GetUserPasswordByUserId(int Id)
        {
            return oUserDBA.GetUserPasswordByUserId(Id);
        }

        /// <summary>
        /// Gets user by user id
        /// </summary>
        /// <param name="sUserID"></param>
        /// <returns></returns>
        public User GetUserByUserID(string sUserID)
        {
            try
            {
                ICriterion oCriteriaUserName = Expression.Eq("UserName", sUserID);
                ICriterion oCriteriaStatus = Expression.Eq("Status", Resource.ACTIVE_STATUS);
                ICriterion oCriteria = Expression.And(oCriteriaUserName, oCriteriaStatus);
                return oUserDBA.GetUniqueByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User by user name", ex);
                return null;
            }
        }
        /// <summary>
        /// Saves user
        /// </summary>
        /// <param name="oUser"></param>
        /// <returns></returns>
        public bool Save(User oUser)
        {
            return oUserDBA.QuickSaveOrUpdate(ref oUser);
        }

        /// <summary>
        /// Delete User
        /// </summary>
        /// <param name="oUser"></param>
        /// <returns></returns>
        public bool Delete(User oUser)
        {
            return oUserDBA.Delete(oUser);
        }
        /// <summary>
        /// Save User Group Link
        /// </summary>
        /// <param name="oLink"></param>
        /// <returns></returns>
        public bool SaveUserGroupLink(GroupUserLink oLink)
        {
            return oUserGrpDBA.QuickSaveOrUpdate(ref oLink);
        }
        /// <summary>
        /// Delete User Group Link
        /// </summary>
        /// <param name="oLink"></param>
        /// <returns></returns>
        public bool DeleteUserGroupLink(GroupUserLink oLink)
        {
            return oUserGrpDBA.QuickDelete(oLink);
        }
        /// <summary>
        /// Get user group link by Id
        /// </summary>
        /// <param name="sUserID"></param>
        /// <param name="sGroupId"></param>
        /// <returns></returns>
        public GroupUserLink GetUserGroupLinkByIDs(int sUserID, int sGroupId)
        {
            GroupUserLink exampleLink = new GroupUserLink();
            exampleLink.GroupId = sGroupId;
            exampleLink.UserId = sUserID;

            string[] propertiesToExclude = new string[1];
            propertiesToExclude[0] = "Id";

            return oUserGrpDBA.GetUniqueByExample(exampleLink, propertiesToExclude);
        }
        /// <summary>
        /// Gets a unique user with an Email address, 
        /// exception will be thrown if this address appears multiple times. 
        /// </summary>
        /// <param name="EmailAddress"></param>
        /// <returns></returns>
        public User GetUserByEmailAddress(string EmailAddress)
        {
            try
            {
                ICriterion oCriteriaEmail = Expression.Eq("Email", EmailAddress);
                ICriterion oCriteriaStatus = Expression.Eq("Status", Resource.ACTIVE_STATUS);
                ICriterion oCriteria = Expression.And(oCriteriaEmail, oCriteriaStatus);
                return oUserDBA.GetUniqueByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User by Email", ex);
                return null;
            }
        }
        /// <summary>
        /// Get user by password reset hash..
        /// </summary>
        /// <param name="passwordRestHash"></param>
        /// <returns></returns>
        public User GetUserByPasswordResetHash(string passwordRestHash)
        {
            try
            {
                ICriterion oCriteria = Expression.Eq("ResetPasswordHash", passwordRestHash);
                return oUserDBA.GetUniqueByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User by password reset hash", ex);
                return null;
            }
        }

        /// <summary>
        /// Creates random hash, sends Email to user and saves this hash in user. 
        /// </summary>
        /// <param name="EmailAddress"></param>
        /// <param name="sUrl"></param>
        /// <returns></returns>
        public bool ResetUserPassword(User oUser, string sUrl)
        {
            try
            {
                //Assign reset date time.
                oUser.ResetPasswordDateTime = DateTime.Now;
                //Generate random hash, and keeps trying untill it finds a unique
                //one which is not in database.
                string sResetPasswordHash;
                do
                {
                    sResetPasswordHash = Resource.CreateRandomString();
                }
                while (IsRandomHashDuplicate(sResetPasswordHash));

                sResetPasswordHash = sResetPasswordHash.Replace("=", "");
                oUser.ResetPasswordHash = sResetPasswordHash;
                if (SendPasswordResetEmail(oUser, sUrl))
                {
                    //Only save this if mail is sent successfully, 
                    //otherwise user will not get access to the reset link.
                    oUserDBA.QuickSaveOrUpdate(ref oUser);
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                logger.Error("Error while resetting user password. Email: " + oUser.Email + " And user: " + oUser.UserName.ToString(), ex);
                return false;
            }
        }

        /// <summary>
        /// Check Random Hash Duplicate
        /// </summary>
        /// <param name="sResetPasswordHash"></param>
        /// <returns></returns>
        private bool IsRandomHashDuplicate(string sResetPasswordHash)
        {
            if (GetUserByPasswordResetHash(sResetPasswordHash) != null)
                return true;
            else
                return false;
        }
        /// <summary>
        /// Creates Email format and sends mail.
        /// </summary>
        /// <param name="oUser"></param>
        /// <returns></returns>
        private bool SendPasswordResetEmail(User oUser, string sUrl)
        {
            try
            {
                List<string> toAddress = new List<string>();
                string sBody = string.Empty;
                string sSubject = string.Empty;
                toAddress.Add(oUser.Email);
                string passwordResetHashURL = "" + sUrl + "?" + oUser.ResetPasswordHash + "";
                if (mailService.CreateEmailBody(ref sBody, ref sSubject, EmailType.ResetPassword, oUser.FirstName, passwordResetHashURL))
                {
                    if (mailService.SendEmail(toAddress, "", sSubject, sBody))
                        return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                logger.Error("Error while sending password reset email for user :" + oUser.UserName.ToString(), ex);
                return false;
            }
        }

        /// <summary>
        /// Sends user registration Email to admin users.
        /// </summary>
        /// <param name="newUser"></param>
        /// <returns></returns>
        public bool SendUserRegistrationEmailToAdmins(User newUser)
        {
            try
            {
                List<string> toAddress = GetAdminEmailAddresses();
                if (toAddress == null)
                    throw new Exception("No admin user found or error occured while retrieving Emails, please refer to logs.");

                string sBody = string.Empty;
                string sSubject = string.Empty;

                if (mailService.CreateEmailBody(ref sBody, ref sSubject, EmailType.UserRegistrationToAdmin, newUser.FirstName + " " + newUser.LastName, newUser.Email))
                {
                    if (mailService.SendEmail(toAddress, "", sSubject, sBody))
                        return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                logger.Error("Error while sending password reset email for User: " + newUser.UserName.ToString(), ex);
                return false;
            }
        }


        /// <summary>
        /// Send registration Email to user.
        /// </summary>
        /// <param name="newUser"></param>
        /// <returns></returns>
        public bool SendUserRegistrationEmailToUser(User newUser)
        {
            try
            {
                List<string> toAddress = new List<string>();
                toAddress.Add(newUser.Email);

                string sBody = string.Empty;
                string sSubject = string.Empty;

                if (mailService.CreateEmailBody(ref sBody, ref sSubject, EmailType.UserRegistrationToUser, newUser.FirstName + " " + newUser.LastName))
                {
                    if (mailService.SendEmail(toAddress, "", sSubject, sBody))
                        return true;
                    else
                        return false;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception ex)
            {
                logger.Error("Error while sending password reset email for User: " + newUser.UserName.ToString(), ex);
                return false;
            }
        }

        /// <summary>
        /// Get Email addresses of all users in admin Group
        /// </summary>
        /// <returns></returns>
        private List<string> GetAdminEmailAddresses()
        {
            try
            {
                List<string> emailList = new List<string>();
                ICriterion oCriteria = Expression.Eq("GroupId", Resource.ADMIN_GROUP_DB_ID);
                List<GroupUserLink> oo = oUserGrpDBA.GetByCriteria(oCriteria);
                foreach (GroupUserLink oLink in oo)
                {
                    User AdminUser = new User();
                    if(oLink.UserId != 0)
                         AdminUser = GetUser(oLink.UserId);
                    if(AdminUser != null)
                        emailList.Add(AdminUser.Email);
                }
                return emailList;
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting admin users Email addresses", ex);
                return null;
            }
        }

        /// <summary>
        /// Sends registration Emails to users
        /// </summary>
        /// <param name="newUser"></param>
        /// <returns></returns>
        public bool SendUserRegistrationEmails(User newUser)
        {
            if (SendUserRegistrationEmailToAdmins(newUser))
            {
                if (SendUserRegistrationEmailToUser(newUser))
                    return true;
            }
            return false;

        }
        public List<User> GetAllSortedUsers(string sColumn, string sSortOrder)
        {
            return oUserDBA.GetAllSorted(sColumn, sSortOrder);

        }
        /// <summary>
        /// Get UserLinkGroupId By GroupUserLink UserId
        /// </summary>
        /// <param name="sUserId"></param>
        /// <returns></returns>
        public List<GroupUserLink> GetUserLinkGroupIdByGroupUserLinkUserId(int iUserId)
        {
            try
            {
                ICriterion oCriteria = Expression.Eq("UserId", iUserId);
                return oUserGrpDBA.GetByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User Group Id by User Id", ex);
                return null;
            }
        }
        /// <summary>
        /// Get Permission by using Group Id
        /// </summary>
        /// <param name="sGroupId"></param>
        /// <returns></returns>
        public List<GroupPermissionLink> GetUserLinkPermissionLinksByGroupId(int iGroupId)
        {
            try
            {
                ICriterion oCriteria = Expression.Eq("GroupId", iGroupId);
                return oGroupPermissionDBA.GetByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting Permission by using Group Id", ex);
                return null;
            }
        }
        /// <summary>
        /// Search User By Name
        /// </summary>
        /// <param name="sUserName"></param>
        /// <returns></returns>
        public List<User> SearchUserByName(string sUserName)
        {
            try
            {
                ICriterion oCriteria = Expression.Like("UserName", "%" + sUserName + "%");
                return oUserDBA.GetByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User Details by Searching  User :" + sUserName, ex);
                return null;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="oGrdData"></param>
        /// <returns></returns>
        public List<User> GetAllUsersForGrid(ref GridViewData oGrdData)
        {
            return oUserDBA.GetAllForGridView(ref oGrdData);
        }

        public bool GetByName(string sName, ref User oUser)
        {
            oUser = null;
            try
            {
                ICriterion oCriteria = Expression.Eq("UserName", sName);
                oUser = oUserDBA.GetUniqueByCriteria(oCriteria);
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting group by user name", ex);
                return false;
            }
        }

        public List<User> GetManagerByDept(string sDeptID, int iUserType)
        {
            try
            {
                ICriterion oCriteriaDeptID = Expression.Eq("DepartmentId", sDeptID);
                ICriterion oCriteriaUserType = Expression.Eq("UserType", iUserType);
                ICriterion oCriteria = Expression.And(oCriteriaDeptID, oCriteriaUserType);
                return oUserDBA.GetByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User by user name", ex);
                return null;
            }
        }

        public List<User> GetSupervisorByManager(string sDeptId, int iChiefSupID, int iUserType)
        {
            try
            {
                ICriterion oCriteriaDeptID = Expression.Eq("DepartmentId", sDeptId);
                ICriterion oCriteriaChiefSupID = Expression.Eq("ParentId", iChiefSupID);
                ICriterion oCriteriaTemp = Expression.And(oCriteriaDeptID, oCriteriaChiefSupID);

                ICriterion oCriteriaUserType = Expression.Eq("UserType", iUserType);
                ICriterion oCriteria = Expression.And(oCriteriaTemp, oCriteriaUserType);
                return oUserDBA.GetByCriteria(oCriteria);
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting User by user name", ex);
                return null;
            }
        }
    }
}
